# -*- shell-script -*-

# 03adapter - Adapter scanning routines and variables.

# This file is part of the Linux lsvpd package.

# (C) Copyright IBM Corp. 2002, 2003, 2004, 2005, 2006

# Maintained by  Martin Schwenke <martins@au.ibm.com>

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    
# $Id: 03adapter,v 1.1 2006/04/11 18:38:28 emunson Exp $

# Default and common functions.

# This module is always loaded.
true || return 0

######################################################################

adapter_set_ds ()
{
    # Sets: ds

    local bus_type="$1"
    local bus_addr="$2"
    local subtype="$3"

    adapter_set_ds_basic "$subtype"
}

adapter_set_ds_basic ()
{
    # Sets: ds

    local subtype="$1"

    case "$subtype" in
	(scsi)       ds="SCSI I/O Controller"             ;;
	(ethernet)   ds="Ethernet PCI Adapter"            ;;
	(ide)        ds="IDE I/O Controller"              ;;
	(usb)        ds="USB Host Controller"             ;;
	(display)    ds="Display Adapter"                 ;;
	(serial)     ds="Serial Adapter"                  ;;
	(sound)      ds="Multimedia Audio Controller"     ;;
	(fibre)      ds="Fibre Channel Adapter"           ;;
	(raid)       ds="RAID Bus Controller"             ;;
	(sata)       ds="Serial ATA Adapter"              ;;
	(ata)        ds="ATA Adapter"                     ;;
	(iscsi)      ds="iSCSI Adapter"                   ;;
	(scsi_debug) ds="SCSI Debug Fake Adapter"         ;;
	(ibmveth)    ds="IBM Virtual Ethernet"            ;;
	(ibmvscsi)   ds="SCSI I/O Controller" ;;
	(oms)        ds="Unknown Mass Storage Controller" ;;
	(token)      ds="Token Ring Network Controller"   ;;
	#(*)          ds="Unknown Adapter"             ;;
    esac
}

######################################################################

# These will never be overridden, so using multiplexing is wasteful.

adapter_set_os_prefix ()
{
    local subtype="$1"

    case "$subtype" in
	scsi|sata|ata|iscsi|fibre|raid|scsi_debug|ibmvscsi)
	    os_prefix="host"
	    ;;
	ethernet|ibmveth|token)
	    os_prefix="eth"
	    ;;
	(ide)      os_prefix="ide"            ;;
	(usb)      os_prefix="usb"            ;;
	(display)  os_prefix="disp"           ;;
	(serial)   os_prefix="sa"             ;;
	(sound)    os_prefix="controlC"       ;;
	(genpci)   os_prefix="adapter"        ;;
	(*)        os_prefix="${subtype:0:4}" ;;
    esac
}

adapter_set_output_prefix ()
{
    local subtype="$1"

    local os_prefix
    adapter_set_os_prefix "$subtype"
    output_prefix="$os_prefix"

    case "$os_prefix" in
	(host)     output_prefix="scsi"  ;;
	(controlC) output_prefix="sound" ;;
    esac
}

get_os_subdir_adapter ()
{
    local subtype="$1"

    case "$subtype" in
	(ethernet|token) os_subdir="net"    ;;
	*)
	    local os_prefix
	    adapter_set_os_prefix "$subtype"
	    os_subdir="$os_prefix"
    esac
}

adapter_set_minimum_unknown ()
{
    local subtype="$1"

    case "$subtype" in
	(display|serial|sound|genpci) minimum_unknown=0   ;;
	(*)                           minimum_unknown=500 ;;
    esac
}

######################################################################

list_adapters ()
{
    :
}

######################################################################

get_parent_subtype_adapter ()
{
    # Sets: parent_subtype
    parent_subtype=""

    local bus_type="$1"
    local bus_addr="$2"

    parent_subtype="$bus_type"
}

######################################################################

retrieve_vpd_adapter ()
{
    local bus_type="$1"
    local bus_info="$2"
    local tmp_dir="$3"

    local bus_addr="${bus_info#*/}"

    # FIXME: The last 2 function calls need to be pushed into
    # node_handler, but they're not there yet.  Then retrieve_vpd will
    # no longer need to be multiplexed.
    debug_cmd node_handler "get_vpd" "$bus_type" "$bus_info" "$tmp_dir" && \
	adapter_extra_vpd_hook "$bus_type" "$bus_addr" "$tmp_dir" && \
	adapter_retrieve_logicals_vpd "$bus_type" "$bus_addr" "$tmp_dir"
}

adapter_retrieve_logicals_vpd ()
{
    local bus_type="$1"
    local bus_addr="$2"
    local node="$3"
}

adapter_retrieve_logicals_common ()
{
    local bus_type="$1"
    local bus_addr="$2"
    local subtype="$3"
    local node="$4"
    local subdirs="$5"

    # If there's more than one subdirectory, then we have channels to
    # process.  We create VPD for each channel containing only a DS
    # field.
    if [ "$subdirs" != "${subdirs#* }" ] ; then
	local ds
	adapter_set_ds "$bus_type" "$bus_addr" "$subtype"
	[ "$ds" ] || return 1  ### !!!

	local i
	for i in $subdirs ; do
	    local vpd_dir
	    vpd_dir_set_hook "${node}/${i}"
	    vpd_field_add_value "$vpd_dir" "DS" "${ds} Channel"
	    >"${node}/${i}/lsvpd,logical"
	    echo -n "$subtype" >"${node}/${i}/lsvpd,subtype"
	done
    fi

    return 0
}

get_yl_adapter_logical ()
{
    # Sets: yl

    local bus_type="$1"
    local bus_addr="$2"
    local source_node="$3"
    local node="$4"

    yl="${bus_addr}/${num}"
}

process_logicals_adapter ()
{
    local bus_type="$1"
    local bus_addr="$2"
    local source_node="$3"
    local node="$4"
    local fc="$5"

    local f="${node}/lsvpd,logicals"
    [ -r "$f" ] || return
    local num subdir
    while read num subdir ; do
	local d="${node}/${subdir}"
	if [ -d "$d" ] ; then
	    echo -n "${bus_type}/${bus_addr}/${num}" >"${d}/lsvpd,bus-info"
	    vpd_node_override_value "$d" "FC" "$fc"
	    backlink_parent_node "$d" "$node"
	    local yl
	    get_yl "adapter_logical" \
		"$bus_type" "$bus_addr" "${source_node}/${subdir}" "$d"
	    [ -n "$yl" ] && \
		vpd_node_override_value "$d" "YL" "$yl"
	fi
    done <"$f"
}

######################################################################

# This function acts as an optimised alias to call the sometimes
# unnecessarily complex get_source_node_adapter.
adapter_get_source_node ()
{
    # Sets: source_node

    local bus_type="$1"
    local bus_addr="$2"
    local subtype="$3"

    local parent_subtype
    get_parent_subtype_adapter "$bus_type" "$bus_addr"

    get_source_node "adapter" \
	"$parent_subtype" "${bus_type}/${bus_addr}" "$subtype"
}

######################################################################

adapter_synthesise_ax ()
{
    # Sets: ax
    ax=""

    local subtype="$1"
    
    local output_prefix
    adapter_set_output_prefix "$subtype"
    local minimum_unknown sequence_number
    adapter_set_minimum_unknown "$subtype"
    set_sequence_number_hook "adapter_${output_prefix}" $minimum_unknown
    ax="${output_prefix}${sequence_number}"
}

names_to_ax_adapter ()
{
    # Sets: ax
    ax=""

    local subtype="$1"
    local names="$2"

    local os_prefix
    adapter_set_os_prefix "$subtype"

    local output_prefix
    adapter_set_output_prefix "$subtype"

    local lastnum=-999 # Never matched below.
    local n
    for n in $names ; do
	# If possible, replace $os_prefix with $output_prefix.
        [ "$os_prefix" != "$output_prefix" -a \
	    "${n#${os_prefix}}" != "$n" ] && \
	    n="${output_prefix}${n#${os_prefix}}"

	# If $n begins with $output_prefix (rather than something
	# random), we can hopefully collate the names using ranges of
	# numbers.
        if [ "${n#${output_prefix}}" != "$n" ] ; then
            local num="${n#${output_prefix}}"
            # FIXME: No guarantee $num us a number.
            if [ $num -eq $(($lastnum + 1)) ] ; then
                ax="${ax%-${lastnum}}-${num}"
            else
                ax="${ax},${n}"
            fi
            lastnum=$num
        else
            ax="${ax},${n}"
            lastnum=-999
        fi
    done
    ax="${ax#,}" # Remove leading ',' if added.

    [ -n "$ax" ] || adapter_synthesise_ax "$subtype"
}

do_logicals_ax_adapter ()
{
    local subtype="$1"
    local node="$2"
    local names="$3"

    local f="${node}/lsvpd,logicals"
    [ -r "$f" ] || return

    if [ "$names" = "${names#* }" ] ; then
	# If just one name, then the logicals are named by adding ":$num".
	local ax
	names_to_ax_adapter "$subtype" "$names"
	[ -n "$ax" ] || return  ### !!!
	
	local num subdir
	while read num subdir ; do
	    local d="${node}/${subdir}"
	    if [ -d "$d" ] ; then
		crosslink "adapter" "$subtype" "$d" "${names}:${num}"
		vpd_node_override_value "$d" "AX" "${ax}:${num}"
	    fi
	done <"$f"
    else
	local n
	for n in $names ; do
	    # Do this 1st - we need to read a line for each name.
	    local num subdir
	    read num subdir

	    # For various reasons (sysfs versus device-tree) there can
	    # be more items in $names than lines in $f.  Therefore, if
	    # we fail to read a line from $f we finish.
	    [ -n "$num" -a -n "$subdir" ] || break

	    # Now build AX field.
	    local ax
	    names_to_ax_adapter "$subtype" "$n"
	    [ -n "$ax" ] || continue

	    local d="${node}/${subdir}"
	    if [ -d "$d" ] ; then
		vpd_node_override_value "$d" "AX" "$ax"
		crosslink "adapter" "$subtype" "$d" "$n"
	    fi
	done <"$f"
    fi
}
